package com.youxin.chat.pay.service.channel.impl.yufu.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.youxin.base.BaseResultCode;
import com.youxin.chat.pay.model.ChannelAccount;
import com.youxin.chat.pay.service.channel.impl.yufu.request.YufuParamPack;
import com.youxin.chat.pay.utils.RSAUtils;
import com.youxin.exception.SystemException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

public class YufuSignUtil {
    private static final Logger logger = LoggerFactory.getLogger(YufuSignUtil.class);

    public static YufuParamPack buildParamRequestWithMd5(Object object, ChannelAccount channelAccount) {
        String jp = JSONObject.toJSONString(object);
        logger.info("Param Json String:[{}]", jp);
        String data = new String(Base64.encodeBase64(jp.getBytes()));
        String md5Input = buildMd5Params(jp);
        logger.debug("md5Input={}", md5Input);
        try {
            String md5String = DigestUtils.md5Hex(md5Input);
            long timestamp = System.currentTimeMillis();
            String signInput = md5String + "|" + timestamp;
            PrivateKey privateKey = RSAUtils.loadPrivateKey(channelAccount.getRsaKeyPri());

            String sign = new String(Base64.encodeBase64(RSAUtils.doSignature(signInput, privateKey, "utf-8")));
            logger.debug("AESKEY Sign With RSA,sign result=[{}]", sign);
            return new YufuParamPack(data, String.valueOf(timestamp), sign);
        } catch (Exception e) {
            logger.error("加密失败，data=", jp, e);
            throw new SystemException(BaseResultCode.COMMON_FAIL, "裕福加签失败");
        }

    }


    public static <T> T unPackWithMd5(String result,Class<T> cls,ChannelAccount channelAccount) throws Exception{
        YufuParamPack paramPack = JSONObject.parseObject(result,YufuParamPack.class);
        PublicKey publicKey = RSAUtils.loadPubKeyByStr(channelAccount.getRsaKeyPub());

        String jp = new String(Base64.decodeBase64(paramPack.getData()));
        logger.info("Decrypt Data with RSA Result, result=[{}]", jp);
        String md5Input = buildMd5Params(jp);
        logger.info("md5Input={}", md5Input);
        String md5String = DigestUtils.md5Hex(md5Input);
        long timestamp = Long.parseLong(paramPack.getEnc());
        String signInput = md5String + "|" + timestamp;
        boolean verified = RSAUtils.verfySha1WithRsa( Base64.decodeBase64(paramPack.getSign().getBytes()),signInput.getBytes(),publicKey);
        logger.info("verified={}", verified);
        if (!verified) {
            throw new SystemException(BaseResultCode.COMMON_FAIL, "裕福验签失败");        }
        return JSON.parseObject(jp, cls);
    }

    private static String buildMd5Params(String str) {
        Map<String, String> map = (Map) JSON.parseObject(str, new TypeReference<Map<String, String>>() {
        }, new Feature[0]);
        StringBuilder result = new StringBuilder(256);
        Map<String, String> sortedMap = new TreeMap(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        sortedMap.putAll(map);
        Map.Entry<String, String> me = null;
        String val = null;
        Iterator it = sortedMap.entrySet().iterator();

        while (it.hasNext()) {
            me = (Map.Entry) it.next();
            val = (String) me.getValue();
            if (val != null) {
                val = val.trim();
                if (!"".equals(val)) {
                    result.append(val);
                }
            }
        }

        return result.toString();
    }


    /**
     * 更是化参数
     *
     * @param map
     * @return
     */
    public static String getParameter(Map<String, Object> map) {
        StringBuilder sb = new StringBuilder();
        //Map<String, String> map = BeanUtil.objectToMap(obj);
        map.remove("class");
        for (String key : map.keySet()) {
            sb.append(key + "=" + map.get(key) + "&");
        }
        return sb.toString();
    }
}
